﻿using System;

using zijian666.Abstractions;
using zijian666.Convertor.Base;
using zijian666.Core;
using zijian666.ConvertExtensions;

namespace zijian666.Convertor;

/// <summary>
/// <seealso cref="TimeSpan" /> 转换器
/// </summary>
internal class TimeSpanConvertor : FromConvertor<TimeSpan>
                                 , IFrom<string, TimeSpan>
                                 , IFrom<object, TimeSpan>
                                 , IFrom<IConvertible, TimeSpan>
                                 , IFromTime<TimeSpan>
{
    /// <summary>
    /// 时间格式化字符
    /// </summary>
    private static readonly string[] FORMATS = [
        "hhmmss",
        "hh\\:mm\\:ss",
        "hhmmssfff",
        "hh\\:mm\\:ss\\.fff",
        "hhmmssffffff",
        "hh\\:mm\\:ss\\.ffffff",
    ];

    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, string input)
    {
        var s = input.Trim();
        if (TimeSpan.TryParse(s, out var result))
        {
            return result;
        }
        foreach (var format in FORMATS)
        {
            if (s.Length == format.Length)
            {
                if (TimeSpan.TryParseExact(s, format, null, System.Globalization.TimeSpanStyles.None, out result))
                {
                    return result;
                }
                break;
            }
        }
        var r = context.Convert<DateTimeOffset>(input);
        if (r.Success)
        {
            return From(context, r.Value);
        }
        return context.ConvertFail(this, input, r.Exception);
    }

    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, IConvertible input)
    {
        if (input.GetTypeCode() == TypeCode.DateTime)
        {
            var time = input.ToDateTime(context.Settings.CultureInfo);
            return new TimeSpan(0, time.Hour, time.Minute, time.Second, time.Millisecond);
        }
        return context.ConvertFail(this, input);
    }

    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, DateTimeOffset input) => input.LocalDateTime.TimeOfDay;

    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, DateTime input) => input.TimeOfDay;

    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, TimeSpan input) => input;

#if NET5_0_OR_GREATER
    public ConvertResult<TimeSpan> From(IConvertContext context, TimeOnly input) => new(input.ToTimeSpan());

    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, DateOnly input) => context.ConvertFail(this, input);

#endif
    /// <inheritdoc/>
    public ConvertResult<TimeSpan> From(IConvertContext context, object input)
    {
        var r = context.Convert<DateTimeOffset>(input);
        if (r.Success)
        {
            return From(context, r.Value);
        }
        return context.ConvertFail(this, input, r.Exception);
    }
}
